﻿using Kimd.Common;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace Kimd.Communication
{
    
    public class TCPServer : ISocketServer, IDisposable,ICommunicationShow
    {
        #region Properties
        private bool Disposed { get; set; } = false;
        private readonly object objLock = new object();
        private readonly object objClient = new object();
        /// <summary>
        /// 服务器instance
        /// </summary>
        private TcpListener _listener;
        private ManualResetEvent ReceiveFinishEvent = new ManualResetEvent(true);
        private volatile string ReceiveDataStr = string.Empty;
        private volatile byte[] ReceiveDataByte = new byte[] { };
        /// <summary>
        /// IP
        /// </summary>
        public string IP { get; set; }
        /// <summary>
        /// Port
        /// </summary>
        public int Port { get; set; }

        [XmlIgnore]
        public Encoding ReceiveEncoding
        {
            get
            {
                Encoding encoding = Encoding.Default;
                switch (Config.ReceiveEncoding)
                {
                    case EncodingEnumStr.ASCII:
                        encoding = Encoding.ASCII;
                        break;
                    case EncodingEnumStr.Default:
                        encoding = Encoding.Default;
                        break;
                    case EncodingEnumStr.UTF8:
                        encoding = Encoding.UTF8;
                        break;
                    case EncodingEnumStr.UTF32:
                        encoding = Encoding.UTF32;
                        break;
                    case EncodingEnumStr.Unicode:
                        encoding = Encoding.Unicode;
                        break;
                }
                return encoding;
            }
        }
        /// <summary>
        /// 发送解码
        /// </summary>
        [XmlIgnore]
        public Encoding SendEncoding
        {
            get
            {
                Encoding encoding = Encoding.Default;
                switch (Config.SendEncoding)
                {
                    case EncodingEnumStr.ASCII:
                        encoding = Encoding.ASCII;
                        break;
                    case EncodingEnumStr.Default:
                        encoding = Encoding.Default;
                        break;
                    case EncodingEnumStr.UTF8:
                        encoding = Encoding.UTF8;
                        break;
                    case EncodingEnumStr.UTF32:
                        encoding = Encoding.UTF32;
                        break;
                    case EncodingEnumStr.Unicode:
                        encoding = Encoding.Unicode;
                        break;
                }
                return encoding;
            }
        }

        /// <summary>
        /// 配置文件
        /// </summary>
        public TCPServerConfig Config { get; set; } = new TCPServerConfig();
       
        [XmlIgnore]
        public List<TcpClientState> TCPClients { get; set; } = new List<TcpClientState>();
        [XmlIgnore]
        public bool IsStart { get; private set; } = false;
        [XmlIgnore]
        public bool IsConnected { get { return TCPClients?.Count() > 0; } }

        #endregion

        #region Event
        /// <summary>
        /// 客户端连接事件
        /// </summary>
        public event EventHandler<TcpClientStateEventArgs> Connected;
        protected void OnConnected(TcpClient tcpClient)
        {
            if (Connected != null)
                Connected(this, new TcpClientStateEventArgs(tcpClient));
        }
        /// <summary>
        /// 客户端断开事件
        /// </summary>
        public event EventHandler<TcpClientStateEventArgs> Disconnected;

        protected void OnDisconnected(TcpClient tcpClient)
        {
            if (Disconnected != null)
                Disconnected(this, new TcpClientStateEventArgs(tcpClient));
        }
        /// <summary>
        /// 服务器接收事件
        /// </summary>
        public event EventHandler<TcpServerDatagramReceivedEventArgs<byte[]>> DatagramReceived;

        protected void OnDatagramReceived(TcpServerDatagramReceivedEventArgs<byte[]> e)
        {
            if (DatagramReceived != null)
                DatagramReceived(this, e);
        }
        /// <summary>
        /// 运行显示事件
        /// </summary>
        public event Action<string> CommRunDisplay;
        public event Action<string> ClientConnected;
        protected void OnCommRunDisplay(string str)
        {
            if (CommRunDisplay != null)
                CommRunDisplay(str);
        }
        [XmlIgnore]
        public ConcurrentQueue<string> ReceivedStringQueue { get; set; } = new ConcurrentQueue<string>();
        #endregion

        #region Constructor
        public TCPServer()
        {
            Init();
        }
        public TCPServer(string name):this()
        {
            Name = name;
        }

        public TCPServer(IPAddress ipAddress, string _port, string _name = "") : this()
        {
            int port;
            if (int.TryParse(_port, out port))
                Config.Port = port;
            Config.IP = ipAddress.ToString();
            Name = _name;
        }

        public TCPServer(string _ip, string _port, string _name = "") : this()
        {
            IPAddress iP; int port;
            if (IPAddress.TryParse(_ip, out iP))
                Config.IP = _ip;
            if (int.TryParse(_port, out port))
                Config.Port = port;
            Name = _name;
        }

        public TCPServer(int port, string _name = "") : this(IPAddress.Any, port.ToString(), _name)
        {

        }
        #endregion

        #region Method
        private void Init()
        {
            if (string.IsNullOrEmpty(IP))
                this._listener = new TcpListener(IPAddress.Any, this.Port);
            else
                this._listener = new TcpListener(IPAddress.Parse(IP), this.Port);
        }
        /// <summary>
        /// 打开服务器
        /// </summary>
        /// <returns></returns>
        public bool Start()
        {
            bool rtn = false;
            try
            {
                if (!IsStart)
                {
                    Init();
                    _listener.Start();
                    OnCommRunDisplay($"{Name}服务器开始监听");
                    _listener.BeginAcceptSocket(new AsyncCallback(HandleTcpClientAccepted), _listener);
                    IsStart = true;
                    rtn = true;
                }
                else
                    rtn = true;
            }
            catch (Exception ex)
            {
                OnCommRunDisplay($"{Name}服务器打开失败,失败原因:{ex.Message}");
            }
            return rtn;
        }

        private void HandleTcpClientAccepted(IAsyncResult ar)
        {
            try
            {
                TcpListener tcpListener = ar.AsyncState as TcpListener;
                TcpClient tcpClient = tcpListener.EndAcceptTcpClient(ar);
                OnCommRunDisplay("成功连接客户端:" + tcpClient.Client.RemoteEndPoint.ToString());
                if (ClientConnected != null)
                    ClientConnected("成功连接客户端:" + tcpClient.Client.RemoteEndPoint.ToString());
                byte[] buffer = new byte[tcpClient.ReceiveBufferSize];
                TcpClientState internalClient = new TcpClientState(tcpClient, buffer);
                lock (objClient)
                {
                    TcpClientState client = TCPClients.FirstOrDefault(o => o.EndPoint == tcpClient.Client.RemoteEndPoint.ToString());
                    if (client != null)
                        TCPClients.Remove(client);
                    TCPClients.Add(internalClient);
                    OnConnected(tcpClient);
                }
                NetworkStream networkStream = internalClient.NetworkStream;
                networkStream.BeginRead(internalClient.Buffer, 0, internalClient.Buffer.Length, new AsyncCallback(HandleDatagramReceived), internalClient);
                tcpListener.BeginAcceptSocket(new AsyncCallback(HandleTcpClientAccepted), ar.AsyncState);
            }
            catch (Exception ex)
            {
                //  OnCommRunDisplay(ex.Message);
                IsStart = false;
            }
        }

        private void HandleDatagramReceived(IAsyncResult ar)
        {
            if (IsStart)
            {
                TcpClientState internalClient = ar.AsyncState as TcpClientState;
                NetworkStream networkStream = internalClient.NetworkStream;
                int numOfReadBytes = 0;
                try
                {
                    numOfReadBytes = networkStream.EndRead(ar);
                }
                catch
                {
                    numOfReadBytes = 0;
                }
                if (numOfReadBytes == 0)
                {
                    lock (this.TCPClients)
                    {

                        TcpClientState client = TCPClients.FirstOrDefault(o => o.EndPoint == internalClient.EndPoint);
                        if (client != null)
                            TCPClients.Remove(client);
                        OnDisconnected(internalClient.TcpClient);
                        OnCommRunDisplay("客户端：" + internalClient.EndPoint + "断开连接");
                        return;
                    }
                }
                byte[] receivedBytes = new byte[numOfReadBytes];

                Buffer.BlockCopy(internalClient.Buffer, 0, receivedBytes, 0, numOfReadBytes);
                ReceiveDataByte = receivedBytes;
                ReceiveDataStr = ReceiveEncoding.GetString(receivedBytes);
                TcpServerDatagramReceivedEventArgs<byte[]> e =
                    new TcpServerDatagramReceivedEventArgs<byte[]>(Name, internalClient.EndPoint, ReceiveDataStr, ReceiveDataByte);
                OnDatagramReceived(e);
                ReceivedStringQueue.Enqueue(ReceiveDataStr);
                OnCommRunDisplay($"{Name}<--{ internalClient.EndPoint}：{ReceiveDataStr}");
                ReceiveFinishEvent.Set();
                networkStream.BeginRead(internalClient.Buffer, 0, internalClient.Buffer.Length, new AsyncCallback(HandleDatagramReceived), internalClient);
            }
        }
        protected void ResetReceiveValue()
        {
            ReceiveFinishEvent.Set();
            ReceiveDataByte = new byte[] { };
            ReceiveDataStr = string.Empty;
        }
        protected void ResetSendValue()
        {
            ReceiveFinishEvent.Reset();
            ReceiveDataByte = new byte[] { };
            ReceiveDataStr = string.Empty;
        }

        /// <summary>
        /// 服务器关闭
        /// </summary>
        /// <returns></returns>
        public bool Close()
        {
            if (IsStart)
            {
                _listener.Stop();
                IsStart = false;
                ReceiveFinishEvent.Set();
                lock (objClient)
                {
                    this.TCPClients.ForEach(o =>
                    {
                        o.TcpClient.Client.Disconnect(false);
                    });
                    this.TCPClients.Clear();
                }
                OnCommRunDisplay($"{Name}服务器关闭");
            }
            return true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!this.Disposed)
            {
                if (disposing)
                {
                    try
                    {
                        Close();
                        if (_listener != null)
                        {
                            _listener=null;
                        }
                    }
                    catch
                    {
                    }
                }
                Disposed = true;
            }
        }
        /// <summary>
        /// 发送信息，返回发送状态,接收值
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="data"></param>
        /// <param name="receive"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public bool Send(TcpClient tcpClient, byte[] data, out string receive, int timeout = 500)
        {
            bool result = false;
            receive = string.Empty;
            if (Send(tcpClient, data))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 发送信息，返回发送状态,接收值
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="data"></param>
        /// <param name="receive"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public bool Send(TcpClient tcpClient, byte[] data, out byte[] receive, int timeout = 500)
        {
            bool result = false;
            receive = new byte[] { };
            if (Send(tcpClient, data))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
            }
            return result;
        }
        /// <summary>
        /// 发送信息，返回发送状态
        /// </summary>
        /// <param name="tcpClient"></param>
        /// <param name="data"></param>
        /// <param name="receive"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public bool Send(TcpClient tcpClient, byte[] data)
        {
            if (tcpClient == null)
                throw new ArgumentNullException("tcpClient");
            if (data == null)
                throw new ArgumentNullException("datagram");
            bool result = false;
            try
            {
                if (this.IsStart)
                {
                    if (tcpClient != null)
                    {
                        TcpClientState client = this.TCPClients.FirstOrDefault(o => o.EndPoint == tcpClient?.Client.RemoteEndPoint.ToString());
                        if (client != null)
                        {
                            lock (objLock)
                            {
                                ResetSendValue();
                                tcpClient.GetStream().BeginWrite(
                                data, 0, data.Length, new AsyncCallback(HandleDatagramWritten), tcpClient
                                );
                                OnCommRunDisplay($" {Name}->{tcpClient.Client.RemoteEndPoint.ToString()}:{data.ByteArrayToString()}");
                                result = true;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ResetReceiveValue();
                OnCommRunDisplay($" {Name}->{tcpClient.Client.RemoteEndPoint.ToString()}:发送失败,失败原因:{ex.Message}");
            }
            return result;
        }

        private void HandleDatagramWritten(IAsyncResult ar)
        {
            (ar.AsyncState as TcpClient).GetStream().EndWrite(ar);
        }

        public bool Send(TcpClient tcpClient, string data, out string receive, int timeout = 500)
        {
            bool result = false;
            receive = string.Empty;
            if (Send(tcpClient, data))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataStr;
                        result = true;
                    }
                }
            }
            return result;
        }

        public bool Send(TcpClient tcpClient, string data, out byte[] receive, int timeout = 500)
        {
            bool result = false;
            receive = new byte[] { };
            if (Send(tcpClient, data))
            {
                if (timeout <= 0)
                {
                    if (ReceiveFinishEvent.WaitOne(500))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
                else
                {
                    if (ReceiveFinishEvent.WaitOne(timeout))
                    {
                        receive = ReceiveDataByte;
                        result = true;
                    }
                }
            }
            return result;
        }

        public bool Send(TcpClient tcpClient, string data)
        {
            if (tcpClient == null)
                throw new ArgumentNullException("tcpClient");
            if (data == null)
                throw new ArgumentNullException("datagram");
            bool result = false;
            try
            {
                if (this.IsStart)
                {
                    if (tcpClient != null)
                    {
                        TcpClientState client = this.TCPClients.FirstOrDefault(o => o.EndPoint == tcpClient?.Client.RemoteEndPoint.ToString());
                        if (client != null)
                        {
                            lock (objLock)
                            {
                                ResetSendValue();
                                byte[] datas = SendEncoding.GetBytes(data);
                                tcpClient.GetStream().BeginWrite(
                                datas, 0, data.Length, new AsyncCallback(HandleDatagramWritten), tcpClient
                                );
                                OnCommRunDisplay($" {Name}->{tcpClient.Client.RemoteEndPoint.ToString()}:{data}");
                                result = true;
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                ResetReceiveValue();
                OnCommRunDisplay($" {Name}->{tcpClient.Client.RemoteEndPoint.ToString()}:发送失败,失败原因:{ex.Message}");
            }
            return result;
        }
        /// <summary>
        /// 所有客户端发送信息
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool SendAll(string data)
        {
            bool result = true;
            this.TCPClients.ForEach(o =>
            {
                result = result && Send(o.TcpClient, data);
            });
            return result;
        }
        /// <summary>
        /// 所有客户端发送信息
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public bool SendAll(byte[] data)
        {
            bool result = true;
            this.TCPClients.ForEach(o =>
            {
                result = result && Send(o.TcpClient, data);
            });
            return result;
        }
        public void EmptyBuffer()
        {
            while (!ReceivedStringQueue.IsEmpty)
            {
                string temp = "";
                ReceivedStringQueue.TryDequeue(out temp);
            }
        }
        #endregion

        #region ICommunicationShow
        public CommTypeEnum CommType { get; } = CommTypeEnum.TCPServer;

        /// <summary>
        /// 指令合集
        /// </summary>
        [XmlIgnore]
        public SerializableDictionary<string, string> SendCmd
        {
            get
            {
                return Config.SendCmd;
            }
            set
            {
                Config.SendCmd = value;
            }
        }
     
        public string Name { get; set; } = string.Empty;
        [XmlIgnore]
        public bool IsOpenFlag => IsStart;

        public object GetConfig()
        {
            return Config;
        }

        public Form GetDebugForm()
        {
            return new CommunicationPage(this);
        }

        public List<string> GetShowList()
        {
            List<string> clients = new List<string>();
            foreach (var item in TCPClients)
            {
                clients.Add($"{item.EndPoint}");
            }
            return clients;
        }

        public string GetTreeNodeShowStr()
        {
            return $"[TCPServer]({IP}:{Port})";
        }

        public void SetConfig(object config)
        {
            if (config is TCPServerConfig)
            {
                Config = config as TCPServerConfig;
            }
        }
        public void SetOpenOrClose(OpenOrClose openOrClose)
        {
            switch (openOrClose)
            {
                case OpenOrClose.Open:
                    Start();
                    break;
                case OpenOrClose.Close:
                    Close();
                    break;
            }
        }
        #endregion
    }
}
